﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NLog;
using planerka.DataAccess.DataService;
using System.Collections;
using System.Web.Configuration;
using System.Text;
using OpenPop.Pop3;
using OpenPop.Mime;
using planerka.Common;
using OpenPop.Pop3.Exceptions;
using System.Text.RegularExpressions;
using planerka.DataAccess.Models;
using System.IO;
using System.Collections.Specialized;
using System.Net;


namespace planerka.Web.Controllers
{
    public class CRONController : Controller
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        // читать сообщения с сервера
        public ActionResult readMail(int? id)
        {
            MessagesService messagesService = new MessagesService();

            Pop3Client pop3Client = new Pop3Client(); 
            Dictionary<int, Message> messages = new Dictionary<int, Message>();

            string hostLogin = WebConfigurationManager.AppSettings["smtpRouterLogin"];
            string hostPassword = WebConfigurationManager.AppSettings["smtpRouterPassword"];
            string pop3RouterHost = WebConfigurationManager.AppSettings["pop3RouterHost"];

            StringBuilder text = new StringBuilder();
            
            #region readMail

            try
            {
                if (pop3Client.Connected)
                    pop3Client.Disconnect();

                pop3Client.Connect(pop3RouterHost, 995, useSsl: true);
                pop3Client.Authenticate(hostLogin, hostPassword);
                int count = pop3Client.GetMessageCount();
                text.Append("Всего сообщений: " +  count.ToString());

                int success = 0;
                int fail = 0;

                int warningLimit = 100;
                if (count > warningLimit)
                    ExceptionUtility.LogException(logger, new Exception(), "",
                    "ВНИМАНИЕ!!! Количество почтовых сообщений в ящике '" + hostLogin + "' превысило " + warningLimit, LogLevel.Warn);

                for (int i = count; i >= 1; i -= 1)
                {

                    try
                    {
                        Message message = pop3Client.GetMessage(i);

                        // Add the message to the dictionary from the messageNumber to the Message
                        messages.Add(i, message);
                        


                        text = text.AppendFormat("<br />Письмо от {0} с темой {1}", message.Headers.From, message.Headers.Subject);

                        bool outputMessagesBody = false;
                        if (outputMessagesBody)
                        {
                            MessagePart mp = message.FindAllTextVersions().FirstOrDefault();

                            if (mp != null)
                            {
                                string sanitizedText = mp.GetBodyAsText(); // PRESERVE!!! tools.SanitizeHtml(tools.removeHTMLTags(mp.GetBodyAsText()), isParanoicMode: true);
                                text = text.Append("<br />" + sanitizedText + "<br />");
                            }
                        }
                        success++;
                    }
                    catch (Exception ex)
                    {
                        ExceptionUtility.LogException(logger, ex,
                            "public ActionResult readMail(int? id)", "Ошибка извлечения письма");

                        fail++;
                    }

                }

                text.Append("<br />Mail received!<br />Successes: " + success + "<br />Failed: " + fail + "<br />");

                if (fail > 0)
                {
                    text.Append(
                                    "Since some of the emails were not parsed correctly (exceptions were thrown)<br />" +
                                    "please consider sending your log file to the developer for fixing.<br />" +
                                    "If you are able to include any extra information, please do so.");
                }
            }
            catch (InvalidLoginException)
            {
                text.Append("<br />The server did not accept the user credentials!");
            }
            catch (PopServerNotFoundException)
            {
                text.Append("<br />The server could not be found");
            }
            catch (PopServerLockedException)
            {
                text.Append("<br />The mailbox is locked. It might be in use or under maintenance. Are you connected elsewhere?");
            }
            catch (LoginDelayException)
            {
                text.Append("<br />Login not allowed. Server enforces delay between logins. Have you connected recently?");
            }
            catch (Exception e)
            {
                text.Append("<br />Error occurred retrieving mail. " + e.Message);
            }
            finally
            {
                // Enable the buttons again
                
            }

            #endregion 

            #region sendMail

            EmailSMTP_Service emailService = new EmailSMTP_Service();
            
            _Repository<message> messageRepo = new _Repository<message>();
            _Repository<task> taskRepo = new _Repository<task>();

            string pattern = @"\[[\s\S]*?(\])";
            MatchCollection results;

            List<int> messageToDelete_Ids = new List<int>();
            emailMessageStatuse _emailMessageStatuse;
            foreach (var emailMessageItem in messages)
            {
                // прочитаем из БД статус почтового сообщения
                _emailMessageStatuse = messagesService.getEmailMessageStatus(emailMessageItem.Value.Headers.MessageId);
                if (_emailMessageStatuse == null)
                {
                    _emailMessageStatuse = new emailMessageStatuse();
                    _emailMessageStatuse.emailMessage_ID = emailMessageItem.Value.Headers.MessageId;
                }

                // если не форвардили или не добавляли
                if (!_emailMessageStatuse.forwarded || !_emailMessageStatuse.addedToTask)
                {
                    MessagePart mp = emailMessageItem.Value.FindAllTextVersions().FirstOrDefault();
                    string sanitizedText = "";
                    string forwardText = "";
                    {
                        sanitizedText = tools.SanitizeHtml(mp.GetBodyAsText(), isMailMode: true);
                        forwardText = mp.GetBodyAsText();
                    }

                    // пропарсить заголовок
                    Guid message_guid = new Guid();

                    // добавим поля DisplayName из набора To
                    string toDisplayNames = "";
                    foreach (var toItem in emailMessageItem.Value.Headers.To)
                        toDisplayNames += toItem.DisplayName;

                    results = Regex.Matches(toDisplayNames, pattern);
                    // перебор
                    foreach (var res in results)
                    {
                        string message_guid_string = res.ToString().Trim().Replace("[", "").Replace("]", "").Replace(" ", ""); //tools.getOnlyNumbers(res.ToString());

                        if (Guid.TryParse(message_guid_string, out message_guid))
                        {
                            // пропарсили guid, ищем сообщение и работаем с ним
                            #region

                            message _message = messageRepo.FindAll(p => p.guid == message_guid).FirstOrDefault();
                            if (_message != null)
                            {
                                // найдем отправителя
                                List<user> taskUsers = messagesService.getUsersForTask(_message.task_ID);
                                user userWasFrom = taskUsers.Where(p => p.ID == _message.from_userID).FirstOrDefault();
                                user userWasTo = taskUsers.Where(p => p.ID == _message.to_userID).FirstOrDefault();

                                // значит можно корреспонденту с таким адресом участвовать 
                                // в переписке по этой задаче 
                                if (userWasFrom != null && userWasTo != null)
                                {
                                    // меняем местами
                                    string emailTo = userWasFrom.email;

                                    List<SelectListItem> attachments = new List<SelectListItem>();

                                    // почистим temp-директорию
                                    FileService.emptyTempDirectory();

                                    // сохраним вложения
                                    string filePath = Server.MapPath("~/temp/") + Guid.NewGuid();
                                    FileInfo file = new FileInfo(filePath);
                                    foreach (var _messageItemPart in emailMessageItem.Value.FindAllAttachments())
                                    {
                                        _messageItemPart.Save(file);
                                        attachments.Add(new SelectListItem { Value = filePath, Text = _messageItemPart.FileName });
                                    }

                                    if (attachments.Count() > 0)
                                        sanitizedText += ("\r\n(к сообщению приложены файлы: " + tools.getStringFromList(attachments.Select(p => p.Text).ToList(), ")\r\n"));

                                    // форвардим почту (проверим - не отправляли ранее?)
                                    int maxForwardAttempts = 2;
                                    if (!_emailMessageStatuse.forwarded && _emailMessageStatuse.forwardAttempts < maxForwardAttempts)
                                    {
                                        _emailMessageStatuse.forwardAttempts++;

                                        _emailMessageStatuse.forwarded =
                                        emailService.ExecuteHtmlSendMail(
                                           Common.Enums.emailLoginTypes.router,
                                           emailTo,
                                           forwardText, emailMessageItem.Value.Headers.Subject, null, attachments);
                                    }

                                    task _task = taskRepo.FindAll(p => p.ID == _message.task_ID).FirstOrDefault();
                                    // делаем проверки
                                    if (_task != null && !_emailMessageStatuse.addedToTask)
                                    {
                                        // добавим сообщение к задаче
                                        _emailMessageStatuse.addedToTask =
                                        new MessagesService().addMessage
                                        (_message.task_ID,
                                        sanitizedText,
                                        _task.taskName,
                                        fromUser_ID: userWasTo.ID,
                                        toUser_ID: userWasFrom.ID,
                                        sendMail: true);
                                    }
                                }

                                // обновим статус
                                messagesService.updateEmailMessageStatus
                                    (_emailMessageStatuse.emailMessage_ID,
                                    _emailMessageStatuse.forwarded,
                                    _emailMessageStatuse.addedToTask, 
                                    _emailMessageStatuse.forwardAttempts);

                                // нашли Guid в заголовке, успешно пропарсили его
                                // потом отфорвардили и добавили сообщение к задаче
                                if (_emailMessageStatuse.forwarded && _emailMessageStatuse.addedToTask)
                                    messageToDelete_Ids.Add(emailMessageItem.Key);

                                break;
                            }

                            #endregion
                        }
                    }
                }

            }

            #endregion 

            #region deleteEmails

            foreach (var messageToDelete_Id_item in messageToDelete_Ids)
            {
                try
                {
                    pop3Client.DeleteMessage(messageToDelete_Id_item);
                }
                catch (Exception ex) 
                {
                
                }
            }
            #endregion

            // реальное удаление писем производится при отключении
            pop3Client.Disconnect();

            ViewData["text"] = text;

            return View();
        }

        // формирование задач на основании периодических заданий
        public ActionResult doPeriodicalTasks()
        {
            ContentResult cr = new ContentResult();

            TaskService taskService = new TaskService();
            int qtyNewTasks = taskService.formNewTasksOnPeriodicalTasks();
            cr.Content = "Сформировано новых задач: " + qtyNewTasks;
            return cr;
        }

        // формирвать очереди на просмотр методов для задач
        public ActionResult formMethodsViewQueues()
        {
            string resultMessage = "<br />Старт: " + DateTime.Now;
            ContentResult cr = new ContentResult();
            string strURL = WebConfigurationManager.AppSettings["URL_getMethodsKeywords"];
            HttpWebRequest GetRequest = (HttpWebRequest)WebRequest.Create(strURL);
            
            try
            {
                //Get response from server
                HttpWebResponse response = (HttpWebResponse)GetRequest.GetResponse();

               

                // Get the stream associated with the response.
                Stream receiveStream = response.GetResponseStream();
                StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
                string tempString = readStream.ReadToEnd();
                byte[] requestByte = Encoding.UTF8.GetBytes(tempString);
                
                // Deserialize
                List<itemKeywords> itemsKeywords = new TaskService().DeserializeMethodsKeywords(requestByte);

                // получить список неиндексированных задач
                List<task> tasksNoIndexing = new _Repository<task>().FindAll(p => !p.isMethodRecomendationIndexed).ToList();
                foreach (var _task in tasksNoIndexing)
                {
                    // Морфологический разбор слова
                    //www.codenet.ru/progr/alg/morf.php
                    string search = _task.taskName + " " + _task.taskDescription;
                    string messages = String.Join(" ", new _Repository<message>().FindAll(p => p.task_ID == _task.ID).Select(p => p.text).ToList());
                    search += (" " + messages);
                    int counter = 0;
                    foreach (var words in itemsKeywords)
                    {
                        
                        string[] _words = words.keywords.Split(',');
                        foreach (var _word in _words)
                        {
                            if (_word.Length < 4) 
                                continue;

                            if (search.Contains(_word))
                            {

                                using (_Repository<methodRecomendation> methodRecomendationRepo = new _Repository<methodRecomendation>())
                                {
                                    methodRecomendation _methodRecomendation = new methodRecomendation();
                                    _methodRecomendation.task_ID = _task.ID;
                                    _methodRecomendation.method_ID = words.item_ID;
                                    methodRecomendationRepo.InsertInstance(_methodRecomendation);

                                    try { methodRecomendationRepo.Save(); }
                                    catch { }
                                }
                                resultMessage += "<br/> Нашли: '" + _word + "'  для задачи: " + _task.ID;
                                counter++;
                                break; // закончили с этим методом проектирования
                            }

                        }
                    }

                    // если ничего не нашли - добавим случайных 5
                    using (_Repository<methodRecomendation> methodRecomendationRepo = new _Repository<methodRecomendation>())
                    {
                        Random rnd = new Random();
                        while (counter < 4)
                        {
                            int myRandom = rnd.Next(itemsKeywords.Count);
                            itemKeywords _itemKeywords = itemsKeywords.Skip(myRandom).FirstOrDefault();
                            if (_itemKeywords != null)
                            {
                                // если такой рекоммендации нет, то добавим 
                                if (methodRecomendationRepo.FindAll(p => p.task_ID == _task.ID && p.method_ID == _itemKeywords.item_ID).Count() == 0)
                                {
                                    methodRecomendation _methodRecomendation = new methodRecomendation();
                                    _methodRecomendation.task_ID = _task.ID;
                                    _methodRecomendation.method_ID = _itemKeywords.item_ID;
                                    methodRecomendationRepo.InsertInstance(_methodRecomendation);
                                    counter++;
                                    try { methodRecomendationRepo.Save(); }
                                    catch { }    
                                }
                            }

                        }
                    }

                    using (_Repository<task> taskRepo = new _Repository<task>())
                    {
                        task editTask = taskRepo.FindAll(p => p.ID == _task.ID).First();
                        editTask.isMethodRecomendationIndexed = true;
                        try { taskRepo.Save(); }
                        catch { }
                    }
                }
                //}
               

                
            }
            catch (Exception ex)
            {
                LogEventInfo logEvent = new LogEventInfo
                {
                    Exception = ex,
                    Level = LogLevel.Error,
                    Message = "ошибка чтения",
                    TimeStamp = DateTime.Now
                };

                LogManager.GetCurrentClassLogger().Log(logEvent);
                resultMessage = "ошибка формирования рекоммендаций";
            }
            resultMessage += "<br/>Финиш: " + DateTime.Now;
            cr.Content = resultMessage;
            return cr;
        }
    }
}
